package com.wsx.ones.user.sequence;

import com.wsx.ones.finalstr.common.CommonFinalUtil;
import com.wsx.ones.finalstr.common.RedisFinalUtil;
import com.wsx.ones.web.util.SpringUtil;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Created by wangshuaixin on 16/12/24.
 */
public class SequenceNum {

    //关于redis的使用
    private static JedisPool jedisPool;
    private static Map<String,Integer> dbMap = new ConcurrentHashMap<String,Integer>();

    //单例的安全实现如下
    private SequenceNum() {
        JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) SpringUtil.getBean("jedisConnectionFactory");
        jedisPool = new JedisPool(jedisConnectionFactory.getPoolConfig(),
                                    jedisConnectionFactory.getHostName(),jedisConnectionFactory.getPort());
        dbMap.put("default", jedisConnectionFactory.getDatabase());
        System.out.print("init one");
    }
    private static class SequenceHelper {
        private static SequenceNum instance = new SequenceNum();
    }
    public static SequenceNum getInstance() {
        return SequenceHelper.instance;
    }


    //下面是需要进行内部缓存处理的数据
    private static Map<String, ConcurrentLinkedQueue<Long>> sequenceMap = new ConcurrentHashMap<String, ConcurrentLinkedQueue<Long>>();
    private static final long SEQUENCE_NUM = 50;


    //获得分布式序列的方法
    public Long getSequenceNum(String sequence) {
        Long sequenceNum = RedisFinalUtil.ERROR_MSG;

        ConcurrentLinkedQueue<Long> sequenceQuene = sequenceMap.get(sequence);

        if (null != sequenceQuene) {
            if (sequenceQuene.isEmpty()) {
                getAndSetQuene(sequence, sequenceQuene);
            }
        } else {
            sequenceQuene = new ConcurrentLinkedQueue<Long>();
            sequenceMap.put(sequence, sequenceQuene);
            getAndSetQuene(sequence, sequenceQuene);
        }
        sequenceNum = sequenceQuene.poll();
        return sequenceNum;
    }

    /**
     * 重构本地缓存的数据
     * @param sequence
     * @param sequenceQuene
     */
    private synchronized void getAndSetQuene(String sequence, ConcurrentLinkedQueue<Long> sequenceQuene) {
        if (!sequenceQuene.isEmpty()) return;
        if (!sequenceMap.get(sequence).isEmpty()) return;
        try {
            Jedis jedis = getJedis(sequence);

            byte [] keys = sequence.getBytes(CommonFinalUtil.CHARSET_STR);
            Transaction trans =jedis.multi();
            Response<Long> start = trans.incr(keys);
            Response<Long> end = trans.incrBy(keys, SEQUENCE_NUM);
            trans.exec();

            closeRedis(jedis);
            for (long i = start.get(); i <= end.get(); i++) {
                sequenceQuene.add(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void closeRedis(Jedis jedis) {
        jedisPool.returnResourceObject(jedis);
    }

    private Jedis getJedis(String sequence) {
        Jedis jedis = jedis = jedisPool.getResource();
        jedis.select(dbMap.containsKey(sequence) ? dbMap.get(sequence) : dbMap.get("default"));
        return jedis;
    }


}
